/* $Id$
 * $URL$
 *
 * Copyright (C) 2009 Scott Sibley <scott@starlon.net>
 * Copyright (C) 2010 Daniel Hiepler <daniel@niftylight.de>
 *
 * This file is part of Blurks-libvisual.
 *
 * Blurks-libvisual is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Blurks-libvisual is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Blurks-libvisual.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <libvisual/libvisual.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

#include "actor_blursk.h"
#include "blursk.h"


/**
 * check if fade-speed string is valid
 * @p speed - fade-speed string
 * @return 0 if invalid, 1 if valid
 */
static int _fade_speed_validate(char *speed)
{
	if
    (
		(strcmp(speed, "No fade") == 0) ||
		(strcmp(speed, "Slow fade") == 0) ||
		(strcmp(speed, "Medium fade") == 0) ||
		(strcmp(speed, "Fast fade") == 0)
	)
        return TRUE;
    
    return FALSE;
}

/**
 * check if flash-style string is valid
 * @p style - flash_style string
 * @return 0 if invalid, 1 if valid
 */
static int _flash_style_validate(char *style)
{
    if
    (
		(strcmp(style, "Full flash") == 0) ||
		(strcmp(style, "Invert flash") == 0) ||
		(strcmp(style, "No flash") == 0) ||
		(strcmp(style, "Random flash") == 0)
	)
        return TRUE;
    
    return FALSE;
}

/**
 * check if overall-effect string is valid
 * @p effect - overall_effect string
 * @return 0 if invalid, 1 if valid
 */
static int _overall_effect_validate(char *effect)
{
    if
    (
		(strcmp(effect, "Bump effect") == 0) ||
		(strcmp(effect, "Anti-fade effect") == 0) ||
		(strcmp(effect, "Ripple effect") == 0) ||
		(strcmp(effect, "Normal effect") == 0)
	)
        return TRUE;
    
    return FALSE;
}

/**
 * check if floaters string is valid
 * @p floaters - floaters string
 * @return 0 if invalid, 1 if valid
 */
static int _floaters_validate(char *floaters)
{
    char *tmp;
    int i = 0;
    
    while((tmp = floaters_name(i)))
    {
        if(strcmp(floaters, tmp) == 0)
            return 1;
        i++;
    }
    
    return 0;
}

/**
 * Check if show_info string is valid
 * @p show_info - show info string
 * @return 0 if invalid, 1 if valid
 */
static int _show_info_validate(char *showinfo)
{
	if
	(
		(strcmp(showinfo, "Never") == 0) ||
		(strcmp(showinfo, "Timed") == 0) ||
		(strcmp(showinfo, "Always") == 0)
	)
		return TRUE;
		
	return FALSE;
}

/**
 * check if blur-style is valid
 * @p style - blur_style string
 * @return 0 if invalid, 1 if valid
 */
static int _blur_style_validate(char *style)
{
    char *tmp;
    int i = 0;
    
    for(i = 0; (tmp = blur_name(i)); i++)
    {
        if(strcmp(style, tmp) == 0)
            return TRUE;
    }
    
    return FALSE;
}

/**
 * check if transition_speed string is valid
 * @p speed - transition speed string
 * @return 0 if invalid, 1 if valid
 */
static int _blur_transition_speed_validate(char *speed)
{
	if
	(
		(strcmp(speed, "Slow") == 0) ||
		(strcmp(speed, "Medium") == 0) ||
		(strcmp(speed, "Fast") == 0)
	)
		return TRUE;
		
	return FALSE;
}

/**
 * check if blur_when string is valid
 * @p when - blur_when string
 * @return 0 if invalid, 1 if valid
 */
static int _blur_when_validate(char *when)
{
    char *tmp;
    int i;
    
    for(i = 0; (tmp = blur_when_name(i)); i++)
    {
        if(strcmp(when, tmp) == 0)
            return TRUE;
    }
    
    return FALSE;
}

/**
 * check if cpu_speed string is valid
 * @p speed - cpu_speed string
 * @return 0 if invalid, 1 if valid
 */
static int _cpu_speed_validate(char *speed)
{
	if
	(
		(strcmp(speed, "Slow") == 0) ||
		(strcmp(speed, "Medium") == 0) ||
		(strcmp(speed, "Fast") == 0)
	)
		return TRUE;
		
	return FALSE;
}

/**
 * check if signal-style string is valid
 * @p style - signal_style string
 * @return 0 if invalid, 1 if valid
 */
static int _signal_style_validate(char *style)
{
	
    int i;
	char *sname;
	
	for(i = 0; (sname = signal_style_name(i)); i++)
	{
		if(strcmp(style, sname) == 0)
			return TRUE;
	}
	
	return FALSE;
}

/**
 * check if plot-style string is valid
 * @p style - signal_style string
 * @return 0 if invalid, 1 if valid
 */
static int _plot_style_validate(char *style)
{
	int i;
	char *sname;
	
	for(i = 0; (sname = render_plotname(i)); i++)
	{
		if(strcmp(style, sname) == 0)
			return TRUE;
	}
	
	return FALSE;
}

/**
 * check if colorstyle string is valid
 * @p name - name of colorstyle
 * @return 0 if invalid, 1 if valid
 */
static int _color_style_validate(char *name)
{
    int i;
    char *oname;
    

    for(i = 0; (oname = color_name(i)); i++)
    {
        if(strcmp(name, oname) == 0)
            return 1;
    }
    
    if(strcmp(name, "Random") == 0)
        return 1;
    
    return 0;
}

/**
 * check if sginal_color string is valid
 * @p color - color string
 * @return 0 if invalid, 1 if valid
 */
static int _color_signal_validate(char *color)
{    
	if
	(
		(strcmp(color, "White signal") == 0) ||
		(strcmp(color, "Normal signal") == 0) ||
		(strcmp(color, "Cycling signal") == 0)
	)
		return TRUE;
		
	return FALSE;
}

/**
 * check if background_color string is valid
 * @p bg - background color string
 * @return 0 if invalid, 1 if valid
 */
static int _color_background_validate(char *bg)
{
	int i;
	char *bname;
	
	for(i = 0; (bname = color_background_name(i)); i++)
	{
		if(strcmp(bg, bname) == 0)
			return TRUE;
	}
	
	return FALSE;
}

/**
 * fill BlurskConfig structure with default values
 */
void config_default(BlurskConfig *conf)
{
	/* tmp structure to conveniently update a BlurskConfig structure */
    struct
    {
		/* default value */
        union 
        { 
			uint32_t uint32;
			int integer;
			char *string; 
		} defval;
		/* type of parameter */
		int type;
		/* pointer to value */
        union
        {
			uint32_t *uint32;
			int *integer;
			char **string;
		} val;
    }entries[] =
    {
		{.defval.integer = 256, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->width},
		{.defval.integer = 128, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->height},
        {.defval.uint32 = 0x00ffff, VISUAL_PARAM_ENTRY_TYPE_COLOR, .val.uint32 = &conf->color},
        {.defval.string = "Rainbow", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->color_style},
        {.defval.string = "Medium fade", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->fade_speed},
        {.defval.string = "Normal signal", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->signal_color},
        {.defval.integer = FALSE, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->contour_lines},
        {.defval.integer = FALSE, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->hue_on_beats},
        {.defval.string = "Black bkgnd", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->background},
        {.defval.string = "Random", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->blur_style},
        {.defval.string = "Medium switch", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->transition_speed},
        {.defval.string = "Full blur", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->blur_when},
        {.defval.string = "No stencil", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->blur_stencil},
        {.defval.integer = FALSE, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->slow_motion},
        {.defval.string = "Stereo spectrum", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->signal_style},
        {.defval.string = "Line", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->plot_style},
        {.defval.integer = TRUE, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->thick_on_beats},
        {.defval.string = "No flash", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->flash_style},
        {.defval.string = "Normal effect", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->overall_effect},
        {.defval.string = "No floaters", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->floaters},
        {.defval.string = "Fast CPU", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->cpu_speed},
        {.defval.string = "Never show info", VISUAL_PARAM_ENTRY_TYPE_STRING, .val.string = &conf->show_info},
        {.defval.integer = 4, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->info_timeout},
        {.defval.integer = 0, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->show_timestamp},
        {.defval.integer = 4, VISUAL_PARAM_ENTRY_TYPE_INTEGER, .val.integer = &conf->beat_sensitivity},
    };
    
    
    /* fill all default-values */
    int i;     
    for(i = 0; i < QTY(entries); i++)
    {
		switch(entries[i].type)
		{
			/* color parameter */
			case VISUAL_PARAM_ENTRY_TYPE_COLOR:
			{
				*entries[i].val.uint32 = entries[i].defval.uint32;
				break;
			}
			
			/* string parameter */
			case VISUAL_PARAM_ENTRY_TYPE_STRING:
			{
				if(*entries[i].val.string)
					visual_mem_free(*entries[i].val.string);
				*entries[i].val.string = strdup(entries[i].defval.string);
				break;
			}
			
			/* integer parameter */
			case VISUAL_PARAM_ENTRY_TYPE_INTEGER:
			{
				*entries[i].val.integer = entries[i].defval.integer;
				break;
			}
			
		}
	}
}

/**
 * generate a new config-string from current config
 */
void config_string_genstring(BlurskPrivate *priv) 
{
    char *string = paste_genstring();

    VisParamContainer *paramcontainer = visual_plugin_get_params(priv->plugin);
    
    VisParamEntry *param = visual_param_container_get(paramcontainer, BLURSK_PARAM_CONFIG_STRING);

    /* don't set if it has already been set */
    if(strcmp(string, visual_param_entry_get_string(param)) != 0)
        visual_param_entry_set_string(param, string);
    
    priv->update_config_string = 0;
}

/**
 * reflect new libvisual values in BlurskConfig structure.
 */
static void _config_load_preset(BlurskPrivate *priv, BlurskConfig *conf)
{
	/* tmp structure to conveniently update a BlurskConfig structure */
    struct
    {
        char *name;
        int type;
        void *val;
    }entries[] =
    {
        {BLURSK_PARAM_COLOR, VISUAL_PARAM_ENTRY_TYPE_COLOR, &conf->color},
        {BLURSK_PARAM_COLOR_STYLE, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->color_style},
        {BLURSK_PARAM_FADE_SPEED, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->fade_speed},
        {BLURSK_PARAM_SIGNAL_COLOR, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->signal_color},
        {BLURSK_PARAM_CONTOUR_LINES, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->contour_lines},
        {BLURSK_PARAM_HUE_ON_BEATS, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->hue_on_beats},
        {BLURSK_PARAM_BACKGROUND, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->background},
        {BLURSK_PARAM_BLUR_STYLE, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->blur_style},
        {BLURSK_PARAM_TRANSITION_SPEED, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->transition_speed},
        {BLURSK_PARAM_BLUR_WHEN, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->blur_when},
        {BLURSK_PARAM_BLUR_STENCIL, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->blur_stencil},
        {BLURSK_PARAM_SLOW_MOTION, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->slow_motion},
        {BLURSK_PARAM_SIGNAL_STYLE, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->signal_style},
        {BLURSK_PARAM_PLOT_STYLE, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->plot_style},
        {BLURSK_PARAM_THICK_ON_BEATS, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->thick_on_beats},
        {BLURSK_PARAM_FLASH_STYLE, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->flash_style},
        {BLURSK_PARAM_OVERALL_EFFECT, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->overall_effect},
        {BLURSK_PARAM_FLOATERS, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->floaters},
        {BLURSK_PARAM_CPU_SPEED, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->cpu_speed},
        {BLURSK_PARAM_SHOW_INFO, VISUAL_PARAM_ENTRY_TYPE_STRING, &conf->show_info},
        {BLURSK_PARAM_INFO_TIMEOUT, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->info_timeout},
        {BLURSK_PARAM_SHOW_TIMESTAMP, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->show_timestamp},
        {BLURSK_PARAM_BEAT_SENSITIVITY, VISUAL_PARAM_ENTRY_TYPE_INTEGER, &conf->beat_sensitivity},
    };
    
    
    /* get libvisual param-container */
    VisParamContainer *paramcontainer = visual_plugin_get_params(priv->plugin);
    
    /* walk all config-parameters */
    int i;     
    for(i = 0; i < QTY(entries); i++)
    {
		/* get libvisual parameter-entry of this parameter */
        VisParamEntry *ptmp = visual_param_container_get(paramcontainer, entries[i].name);
        
        
        switch(entries[i].type)
        {
			/* integer parameter */
            case VISUAL_PARAM_ENTRY_TYPE_INTEGER:
            {
                int *integer = entries[i].val;
                
                /* only update if values differ */
                if(*integer != visual_param_entry_get_integer(ptmp))
                    visual_param_entry_set_integer(ptmp, *integer);
                break;
            }
            
            /* color parameter */
            case VISUAL_PARAM_ENTRY_TYPE_COLOR:
            {
                VisColor *color = visual_param_entry_get_color(ptmp);
                VisColor ncolor;
                uint32_t nicolor = (((color->b)<<16) + ((color->g)<<8) + color->r);
                uint32_t *icolor = entries[i].val;
            
                /* only update if values differ */
                if(*icolor != nicolor)
                {
                    visual_color_set(&ncolor, (*icolor&0xFF0000)>>16, (*icolor&0xFF00)>>8, (*icolor&0xFF));
                    visual_param_entry_set_color_by_color(ptmp, &ncolor);
                }
                break;
            }
            
            /* string parameter */
            case VISUAL_PARAM_ENTRY_TYPE_STRING:
            {
                char **string = entries[i].val;
            
                /* only update if values differ */
                if(strcmp(*string, visual_param_entry_get_string(ptmp)) != 0)
                    visual_param_entry_set_string(ptmp, *string);
                break;
            }
        }
    }
}

/**
 * signal main loop to update colormap
 */
static void __color_genmap(BlurskPrivate *priv)
{
    priv->update_colmap = 1;
}

/**
 * parse a blursk-config-string (tm)
 */
static void _change_config_string(BlurskPrivate *priv, char **string, VisParamEntry *p, int *(validator)(void *value)) 
{

    if(!validator || validator(visual_param_entry_get_string(p)))
    {
        BlurskConfig *c;
        
        /* free previous string? */
        if(*string)
            visual_mem_free(*string);
        
        *string = strdup(visual_param_entry_get_string(p));
        
        /* parse the string */
        c = paste_parsestring(*string);

        /* use this configuration */
        _config_load_preset(priv, c);
        
    }
    /* reset to previous value */
    else
        visual_param_entry_set_string(p, *string);
    
    
}

/**
 * callback to change a color parameter (called by config_change_param)
 */
static void _change_color(BlurskPrivate *priv, uint32_t **color, VisParamEntry *p, int *(validator)(void *value))
{
    VisColor *c;
    
    c = visual_param_entry_get_color(p);
    *color = 0;
    *color = (uint32_t *) (((c->r)<<16) + ((c->g)<<8) + c->b);
    priv->update_config_string = 1;
}

/**
 * callback to change a string parameter (called by config_change_param)
 */
static void _change_string(BlurskPrivate *priv, char **string, 
                           VisParamEntry *p, int *(validator)(void *value))
{ 
    visual_log_return_if_fail(visual_param_entry_get_string(p));

    if(!validator || validator(visual_param_entry_get_string(p)))
    {
        /* free previous string? */
        if(*string)
            visual_mem_free(*string);
        
        *string = strdup(visual_param_entry_get_string(p));
        
        priv->update_config_string = 1;
    }
    /* reset to previous value */
    else
        visual_param_entry_set_string(p, *string);
}


/**
 * callback to change a bool parameter (called by config_change_param)
 */
static void _change_bool(BlurskPrivate *priv, int **boolean, VisParamEntry *p, int *(validator)(void *value))
{
    int t = visual_param_entry_get_integer(p);
    
    /* validate boolean */
    if(t == 0 || t == 1)
    {
        *boolean = (int *)  t;
        
        priv->update_config_string = 1;
    }           
    /* reset to previous value */
    else
        visual_param_entry_set_integer(p, **boolean);
}

/**
 * callback to change an integer parameter (called by config_change_param)
 */
static void _change_int(BlurskPrivate *priv, int **integer, VisParamEntry *p, int *(validator)(void *value))
{
    *integer = (int *) visual_param_entry_get_integer(p);
    
    priv->update_config_string = 1;
}

/**
 * user changed a config parameter
 *
 * @p p - libvisual parameter
 * @p c - blursk configuration structure
 */
void config_change_param(BlurskPrivate *priv, VisParamEntry *p)
{
    /**
     * structure defining handler functions for configuration values
     */
    struct
    {
        /* parameter-name */
        char *name;
        /* blursk storage for parameter value */
        void *value;
        /* validator function */
        int (*validator)(void *value);
        /* function called to change parameter */
        void (*change)(BlurskPrivate *priv, void **value, 
                       VisParamEntry *parameter, int (*validator)(void *value));
        /* function called after parameter change */
        void (*postchange)(BlurskPrivate *priv);
    } parms[] =
    {
        {BLURSK_PARAM_COLOR, &config.color, NULL, (void *) _change_color, __color_genmap},
        {BLURSK_PARAM_COLOR_STYLE, &config.color_style, (void *) _color_style_validate, (void *) _change_string, __color_genmap},
        {BLURSK_PARAM_FADE_SPEED, &config.fade_speed, (void *) _fade_speed_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_SIGNAL_COLOR, &config.signal_color, (void *) _color_signal_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_CONTOUR_LINES, &config.contour_lines, NULL, (void *) _change_bool, NULL},
        {BLURSK_PARAM_HUE_ON_BEATS, &config.hue_on_beats, NULL, (void *) _change_bool, NULL},
        {BLURSK_PARAM_BACKGROUND, &config.background, (void *) _color_background_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_BLUR_STYLE, &config.blur_style, (void *) _blur_style_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_TRANSITION_SPEED, &config.transition_speed, (void *) _blur_transition_speed_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_BLUR_WHEN, &config.blur_when, (void *) _blur_when_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_BLUR_STENCIL, &config.blur_stencil, NULL, (void *) _change_string, NULL},
        {BLURSK_PARAM_SLOW_MOTION, &config.slow_motion, NULL, (void *) _change_bool, NULL},
        {BLURSK_PARAM_SIGNAL_STYLE, &config.signal_style, (void *) _signal_style_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_PLOT_STYLE, &config.plot_style, (void *) _plot_style_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_THICK_ON_BEATS, &config.thick_on_beats, NULL, (void *) _change_bool, NULL},
        {BLURSK_PARAM_FLASH_STYLE, &config.flash_style, (void *) _flash_style_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_OVERALL_EFFECT, &config.overall_effect, (void *) _overall_effect_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_FLOATERS, &config.floaters, (void *) _floaters_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_CPU_SPEED, &config.cpu_speed, (void *) _cpu_speed_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_SHOW_INFO, &config.show_info, (void *) _show_info_validate, (void *) _change_string, NULL},
        {BLURSK_PARAM_INFO_TIMEOUT, &config.info_timeout, NULL, (void *) _change_int, NULL},
        {BLURSK_PARAM_SHOW_TIMESTAMP, &config.show_timestamp, NULL, (void *) _change_bool, NULL},
        {BLURSK_PARAM_BEAT_SENSITIVITY, &config.beat_sensitivity, NULL, (void *) _change_int, NULL},
        {BLURSK_PARAM_CONFIG_STRING, &config.config_string, NULL, (void *) _change_config_string, NULL},
    };
    
    
    
    /** look for parameter in our structure */
    int i;
    for(i = 0; i < QTY(parms); i++)
    {
        /* not our parameter? -> continue the quest */
        if(!visual_param_entry_is(p, parms[i].name))
            continue;
        
        /* call this parameters' change handler */
        if(parms[i].change)
            parms[i].change(priv, parms[i].value, p, parms[i].validator);
        
        /* call this parameters' post-change handler */
        if(parms[i].postchange)
            parms[i].postchange(priv);
        
        return;
    }
    
    printf("Unknown blursk param '%s'\n", visual_param_entry_get_name(p));
}


